/*
* Copyright 2022 Beijing Zitiao Network Technology Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/


package net.bytedance.security.app.result

import kotlinx.serialization.Serializable
import net.bytedance.security.app.result.model.AnySerializer
import net.bytedance.security.app.result.model.SecurityVulnerabilityItem
import net.bytedance.security.app.rules.IRule
import net.bytedance.security.app.util.Json
import org.apache.commons.codec.digest.DigestUtils

interface IVulnerability {
    fun toDetail(): Map<String, @Serializable(with = AnySerializer::class) Any>
    val target: List<String>
    val position: String
}


@Serializable
class VulnerabilityItem(val rule: IRule, val url: String, val data: IVulnerability) {
    override fun hashCode(): Int {
        return rule.hashCode() + url.hashCode() + data.hashCode()
    }

    override fun equals(other: Any?): Boolean {
        if (other is VulnerabilityItem) {
            return other.rule == rule && other.url == url && other.data == data
        }
        return false
    }

    override fun toString(): String {
        return "vulnerabilityItem{name:${rule.name},url:$url,data:$data}"
    }

    fun isCompliance(): Boolean {
        return rule.desc.category == "ComplianceInfo"
    }


    fun toSecurityVulnerabilityItem(): SecurityVulnerabilityItem {
        val s = SecurityVulnerabilityItem(rule = this.rule)
        s.possibility = rule.desc.possibility
        val detail: MutableMap<String, @Serializable(with = AnySerializer::class) Any> = HashMap()
        detail.putAll(data.toDetail())
        s.details = detail
        var data = Json.encodeToString(s)
        data += rule.name
        s.oldHash = calculateOldHash(s, rule)
        s.hash = calculateHash(s, rule)
        if (url.isNotEmpty()) {
            detail["url"] = url
        }
        s.details = detail
        s.rule = rule
        return s
    }

    companion object {
        /**
         * manifest
         */
        fun calculateOldHash(s: SecurityVulnerabilityItem, rule: IRule): String {
            var data = Json.encodeToString(s)
            data += rule.name
            return DigestUtils.sha1Hex(data)
        }

        /**
         * prevent duplicate vulnerabilities
         *  1. ruleName
         *  2. source
         *  3. sink
         *  4. if there are no source and sink, use the target
         */
        fun calculateHash(s: SecurityVulnerabilityItem, rule: IRule): String {
            var data = rule.name
            val details = s.details!!
            val position = details["position"]?.toString()
            if (position != null) {
                data += position
            }
            val source = details["Source"]
            val sink = details["Sink"]
            val target = details["target"]
            if (source != null && sink != null && source is List<*> && sink is List<*>) {
                for (i in source.indices) {
                    data += source[i]
                }
                for (i in sink.indices) {
                    data += sink[i]
                }
            } else if (target != null && target is List<*>) {
                for (i in target.indices) {
                    data += target[i]
                }
            }
            return DigestUtils.sha1Hex(data)
        }
    }
}